﻿using System.Collections.Generic;
using System.Collections.Specialized;
using System.Dynamic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SimpleHttpServer.MIME;

namespace SimpleHttpServer.Handler
{
	public abstract class FxHttpComponent : IFormatToString
	{
		public override string ToString()
		{
			return ToString(0);
		}

		public string ToString(int level)
		{
			return new StringFormatter(level, 4).FormatToString(this);
		}

		protected object ConvertToExpondObj(NameValueCollection collection)
		{
			if (collection == null) return null;
			IDictionary<string, object> coll = new ExpandoObject();
			foreach (string name in collection)
			{
				coll[name] = collection[name];
			}

			return coll;
		}
	}

	public class FxHttpContext : FxHttpComponent
	{
		private HttpListenerContext _internalContext;

		private Dictionary<string, object> _dataDic = new Dictionary<string, object>();

		public FxHttpRequest Request { get; }

		public FxHttpResponse Response { get; }

		internal FxHttpContext(HttpListenerContext ctx)
		{
			this._internalContext = ctx;
			Request = new FxHttpRequest(ctx.Request);
			Response = new FxHttpResponse(ctx.Response);
		}

		public void SetData(string key, object val)
		{
			_dataDic[key] = val;
		}

		public object GetData(string key)
		{
			object data;
			_dataDic.TryGetValue(key, out data);
			return data;
		}
	}

	public class FxHttpRequest : FxHttpComponent
	{
		private HttpListenerRequest _internalRequest;

		internal HttpListenerRequest ReqWrapper => _internalRequest;

		public object PathParams { get; internal set;}

		public object QueryParams => ConvertToExpondObj(_internalRequest.QueryString);

		public object Headers => ConvertToExpondObj(_internalRequest.Headers);

		public string Method => _internalRequest.HttpMethod;

		public string Path => _internalRequest.Url.AbsolutePath;

		internal FxHttpRequest(HttpListenerRequest req)
		{
			this._internalRequest = req;
		}

		internal async Task<string> GetRequestBodyAsync()
		{
			if (!_internalRequest.HasEntityBody)
			{
				return null;
			}
			using (var inputStream = new StreamReader(_internalRequest.InputStream, Encoding.UTF8))
			{
				string body = await inputStream.ReadToEndAsync();
				return body;
			}
		}

		public string GetBody()
		{
			return GetRequestBodyAsync().Result;
		}

		public object Json()
		{
			return JObject.Parse(GetBody());
		}
	}

	public class FxHttpResponse : FxHttpComponent
	{
		private HttpListenerResponse _internalResponse;

		internal HttpListenerResponse RespWrapper => _internalResponse;

		public int Status
		{
			get => _internalResponse.StatusCode;
			set => _internalResponse.StatusCode = value;
		}

		private byte[] resBytes;

		internal FxHttpResponse(HttpListenerResponse resp)
		{
			this._internalResponse = resp;
		}

		public void SetHeader(string name, object val)
		{
			switch (name.ToLower())
			{
				case "content-type":
					_internalResponse.ContentType = val.ToString();
					break;
				case "content-length":
					_internalResponse.ContentLength64 = long.Parse(val.ToString());
					break;
				case "content-encoding":
					_internalResponse.ContentEncoding = Encoding.GetEncoding(val.ToString());
					break;
				case "keep-alive":
					_internalResponse.KeepAlive = bool.Parse(val.ToString());
					break;
				default:
					if (_internalResponse.Headers[name] != null)
					{
						_internalResponse.Headers.Set(name, val.ToString());
					}
					else
					{
						_internalResponse.AddHeader(name, val.ToString());
					}
					break;
			}
		}

		public void AllowCrossOrigin()
		{
			SetHeader("Access-Control-Allow-Origin", "*");
			SetHeader("Access-Control-Allow-Methods", "*");
			SetHeader("Access-Control-Allow-Headers", "*");
		}

		public void SetBody(object obj)
		{
			if (obj == null) return;
			string res;
			try
			{
				if (obj.GetType() == typeof(string))
				{
					throw new System.Exception("type not referrence object");
				}
				res = JsonConvert.SerializeObject(obj);
				_internalResponse.ContentType = HttpMimeType.JSON.GetMimeType();
			}
			catch (System.Exception)
			{
				res = obj.ToString();
				_internalResponse.ContentType = HttpMimeType.PLAIN.GetMimeType();
			}

			resBytes = Encoding.UTF8.GetBytes(res);
		}

		internal void ReturnBody()
		{
			if (resBytes == null) return;
			using (var os = _internalResponse.OutputStream)
			{
				os.WriteAsync(resBytes, 0, resBytes.Length);
			}
		}
	}
}
